diff options
-rw-r--r-- | intern/ghost/GHOST_C-api.h | 16 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_C-api.cpp | 33 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_XrAction.cpp | 64 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_XrAction.h | 8 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_XrSession.cpp | 31 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_XrSession.h | 7 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_xr_types.h | 107 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_xr.c | 1536 | ||||
-rw-r--r-- | source/blender/windowmanager/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/windowmanager/WM_api.h | 51 | ||||
-rw-r--r-- | source/blender/windowmanager/xr/intern/wm_xr.c | 1 | ||||
-rw-r--r-- | source/blender/windowmanager/xr/intern/wm_xr_action.c | 99 | ||||
-rw-r--r-- | source/blender/windowmanager/xr/intern/wm_xr_actionmap.c | 565 | ||||
-rw-r--r-- | source/blender/windowmanager/xr/intern/wm_xr_intern.h | 59 |
14 files changed, 2508 insertions, 70 deletions
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index fea5a545807..83c67f83908 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1102,6 +1102,7 @@ int GHOST_XrSyncActions(GHOST_XrContextHandle xr_context, const char *action_set int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_context, const char *action_set_name, const char *action_name, + const char **subaction_path, const int64_t *duration, const float *frequency, const float *amplitude); @@ -1111,7 +1112,8 @@ int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_context, */ void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_context, const char *action_set_name, - const char *action_name); + const char *action_name, + const char **subaction_path); /** * Get action set custom data (owned by Blender, not GHOST). @@ -1126,6 +1128,18 @@ void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_context, const char *action_set_name, const char *action_name); +/** + * Get the number of actions in an action set. + */ +unsigned int GHOST_XrGetActionCount(GHOST_XrContextHandle xr_context, const char *action_set_name); + +/** + * Get custom data for all actions in an action set. + */ +void GHOST_XrGetActionCustomdataArray(GHOST_XrContextHandle xr_context, + const char *action_set_name, + void **r_customdata_array); + #endif /* WITH_XR_OPENXR */ #ifdef __cplusplus diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index 0bc9be26eb1..b1af5c131ab 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -1005,25 +1005,29 @@ int GHOST_XrSyncActions(GHOST_XrContextHandle xr_contexthandle, const char *acti int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name, const char *action_name, + const char **subaction_path, const int64_t *duration, const float *frequency, const float *amplitude) { GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL_RET(xr_session->applyHapticAction( - action_set_name, action_name, *duration, *frequency, *amplitude), - xr_context); + GHOST_XR_CAPI_CALL_RET( + xr_session->applyHapticAction( + action_set_name, action_name, subaction_path, *duration, *frequency, *amplitude), + xr_context); return 0; } void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name, - const char *action_name) + const char *action_name, + const char **subaction_path) { GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL(xr_session->stopHapticAction(action_set_name, action_name), xr_context); + GHOST_XR_CAPI_CALL(xr_session->stopHapticAction(action_set_name, action_name, subaction_path), + xr_context); } void *GHOST_XrGetActionSetCustomdata(GHOST_XrContextHandle xr_contexthandle, @@ -1046,4 +1050,23 @@ void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_contexthandle, return 0; } +unsigned int GHOST_XrGetActionCount(GHOST_XrContextHandle xr_contexthandle, + const char *action_set_name) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL_RET(xr_session->getActionCount(action_set_name), xr_context); + return 0; +} + +void GHOST_XrGetActionCustomdataArray(GHOST_XrContextHandle xr_contexthandle, + const char *action_set_name, + void **r_customdata_array) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL(xr_session->getActionCustomdataArray(action_set_name, r_customdata_array), + xr_context); +} + #endif /* WITH_XR_OPENXR */ diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp index 676a3367ee1..07eb42c14e6 100644 --- a/intern/ghost/intern/GHOST_XrAction.cpp +++ b/intern/ghost/intern/GHOST_XrAction.cpp @@ -208,8 +208,10 @@ GHOST_XrAction::GHOST_XrAction(XrInstance instance, m_subaction_paths.resize(info.count_subaction_paths); for (uint32_t i = 0; i < info.count_subaction_paths; ++i) { - CHECK_XR(xrStringToPath(instance, info.subaction_paths[i], &m_subaction_paths[i]), - (std::string("Failed to get user path \"") + info.subaction_paths[i] + "\".").data()); + const char *subaction_path_str = info.subaction_paths[i]; + CHECK_XR(xrStringToPath(instance, subaction_path_str, &m_subaction_paths[i]), + (std::string("Failed to get user path \"") + subaction_path_str + "\".").data()); + m_subaction_indices.insert({subaction_path_str, i}); } XrActionCreateInfo action_info{XR_TYPE_ACTION_CREATE_INFO}; @@ -373,6 +375,7 @@ void GHOST_XrAction::updateState(XrSession session, void GHOST_XrAction::applyHapticFeedback(XrSession session, const char *action_name, + const char **subaction_path_str, const int64_t &duration, const float &frequency, const float &litude) @@ -386,24 +389,46 @@ void GHOST_XrAction::applyHapticFeedback(XrSession session, XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO}; haptic_info.action = m_action; - for (std::vector<XrPath>::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end(); - ++it) { - haptic_info.subactionPath = *it; - CHECK_XR(xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration), - (std::string("Failed to apply haptic action \"") + action_name + "\".").data()); + if (subaction_path_str != nullptr) { + SubactionIndexMap::iterator it = m_subaction_indices.find(*subaction_path_str); + if (it != m_subaction_indices.end()) { + haptic_info.subactionPath = m_subaction_paths[it->second]; + CHECK_XR( + xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration), + (std::string("Failed to apply haptic action \"") + action_name + "\".").data()); + } + } + else { + for (const XrPath &subaction_path : m_subaction_paths) { + haptic_info.subactionPath = subaction_path; + CHECK_XR( + xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration), + (std::string("Failed to apply haptic action \"") + action_name + "\".").data()); + } } } -void GHOST_XrAction::stopHapticFeedback(XrSession session, const char *action_name) +void GHOST_XrAction::stopHapticFeedback(XrSession session, + const char *action_name, + const char **subaction_path_str) { XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO}; haptic_info.action = m_action; - for (std::vector<XrPath>::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end(); - ++it) { - haptic_info.subactionPath = *it; - CHECK_XR(xrStopHapticFeedback(session, &haptic_info), - (std::string("Failed to stop haptic action \"") + action_name + "\".").data()); + if (subaction_path_str != nullptr) { + SubactionIndexMap::iterator it = m_subaction_indices.find(*subaction_path_str); + if (it != m_subaction_indices.end()) { + haptic_info.subactionPath = m_subaction_paths[it->second]; + CHECK_XR(xrStopHapticFeedback(session, &haptic_info), + (std::string("Failed to stop haptic action \"") + action_name + "\".").data()); + } + } + else { + for (const XrPath &subaction_path : m_subaction_paths) { + haptic_info.subactionPath = subaction_path; + CHECK_XR(xrStopHapticFeedback(session, &haptic_info), + (std::string("Failed to stop haptic action \"") + action_name + "\".").data()); + } } } @@ -511,6 +536,19 @@ void *GHOST_XrActionSet::getCustomdata() return m_custom_data_->custom_data_; } +uint32_t GHOST_XrActionSet::getActionCount() const +{ + return (uint32_t)m_actions.size(); +} + +void GHOST_XrActionSet::getActionCustomdataArray(void **r_customdata_array) +{ + uint32_t i = 0; + for (auto &[name, action] : m_actions) { + r_customdata_array[i++] = action.getCustomdata(); + } +} + void GHOST_XrActionSet::getBindings( std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const { diff --git a/intern/ghost/intern/GHOST_XrAction.h b/intern/ghost/intern/GHOST_XrAction.h index e2a89e87278..73a1cd9cd6a 100644 --- a/intern/ghost/intern/GHOST_XrAction.h +++ b/intern/ghost/intern/GHOST_XrAction.h @@ -103,17 +103,21 @@ class GHOST_XrAction { const XrTime &predicted_display_time); void applyHapticFeedback(XrSession session, const char *action_name, + const char **subaction_path, const int64_t &duration, const float &frequency, const float &litude); - void stopHapticFeedback(XrSession session, const char *action_name); + void stopHapticFeedback(XrSession session, const char *action_name, const char **subaction_path); void *getCustomdata(); void getBindings(std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const; private: + using SubactionIndexMap = std::map<std::string, uint32_t>; + XrAction m_action = XR_NULL_HANDLE; GHOST_XrActionType m_type; + SubactionIndexMap m_subaction_indices; std::vector<XrPath> m_subaction_paths; /** States for each subaction path. */ void *m_states; @@ -145,6 +149,8 @@ class GHOST_XrActionSet { XrActionSet getActionSet() const; void *getCustomdata(); + uint32_t getActionCount() const; + void getActionCustomdataArray(void **r_customdata_array); void getBindings(std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const; private: diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index a29ec1cc560..a63ce5c9344 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -754,6 +754,7 @@ bool GHOST_XrSession::syncActions(const char *action_set_name) bool GHOST_XrSession::applyHapticAction(const char *action_set_name, const char *action_name, + const char **subaction_path, const int64_t &duration, const float &frequency, const float &litude) @@ -768,12 +769,15 @@ bool GHOST_XrSession::applyHapticAction(const char *action_set_name, return false; } - action->applyHapticFeedback(m_oxr->session, action_name, duration, frequency, amplitude); + action->applyHapticFeedback( + m_oxr->session, action_name, subaction_path, duration, frequency, amplitude); return true; } -void GHOST_XrSession::stopHapticAction(const char *action_set_name, const char *action_name) +void GHOST_XrSession::stopHapticAction(const char *action_set_name, + const char *action_name, + const char **subaction_path) { GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); if (action_set == nullptr) { @@ -785,7 +789,7 @@ void GHOST_XrSession::stopHapticAction(const char *action_set_name, const char * return; } - action->stopHapticFeedback(m_oxr->session, action_name); + action->stopHapticFeedback(m_oxr->session, action_name, subaction_path); } void *GHOST_XrSession::getActionSetCustomdata(const char *action_set_name) @@ -813,4 +817,25 @@ void *GHOST_XrSession::getActionCustomdata(const char *action_set_name, const ch return action->getCustomdata(); } +uint32_t GHOST_XrSession::getActionCount(const char *action_set_name) +{ + GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); + if (action_set == nullptr) { + return 0; + } + + return action_set->getActionCount(); +} + +void GHOST_XrSession::getActionCustomdataArray(const char *action_set_name, + void **r_customdata_array) +{ + GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); + if (action_set == nullptr) { + return; + } + + action_set->getActionCustomdataArray(r_customdata_array); +} + /** \} */ /* Actions */ diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index cdeef153fb1..ec15897058f 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -76,14 +76,19 @@ class GHOST_XrSession { bool syncActions(const char *action_set_name = nullptr); bool applyHapticAction(const char *action_set_name, const char *action_name, + const char **subaction_path, const int64_t &duration, const float &frequency, const float &litude); - void stopHapticAction(const char *action_set_name, const char *action_name); + void stopHapticAction(const char *action_set_name, + const char *action_name, + const char **subaction_path); /* Custom data (owned by Blender, not GHOST) accessors. */ void *getActionSetCustomdata(const char *action_set_name); void *getActionCustomdata(const char *action_set_name, const char *action_name); + uint32_t getActionCount(const char *action_set_name); + void getActionCustomdataArray(const char *action_set_name, void **r_customdata_array); private: /** Pointer back to context managing this session. Would be nice to avoid, but needed to access diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index c0928e1519f..2e348ce6855 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -26,6 +26,8 @@ extern "C" { #endif +/* -------------------------------------------------------------------- */ + typedef struct XrSessionSettings { /** Shading settings, struct shared with 3D-View so settings are the same. */ struct View3DShading shading; @@ -68,21 +70,122 @@ typedef enum eXrActionType { XR_VIBRATION_OUTPUT = 100, } eXrActionType; +/** Determines how XR action operators are executed. */ typedef enum eXrOpFlag { XR_OP_PRESS = 0, XR_OP_RELEASE = 1, XR_OP_MODAL = 2, } eXrOpFlag; +typedef enum eXrActionFlag { + /** Action depends on two subaction paths (i.e. two-handed/bimanual action). */ + XR_ACTION_BIMANUAL = (1 << 0), +} eXrActionFlag; + +typedef enum eXrHapticFlag { + /** Whether to apply haptics to corresponding user paths for an action and its haptic action. */ + XR_HAPTIC_MATCHUSERPATHS = (1 << 0), + /** Determines how haptics will be applied ("repeat" is mutually exclusive with + "press"/"release"). */ + XR_HAPTIC_PRESS = (1 << 1), + XR_HAPTIC_RELEASE = (1 << 2), + XR_HAPTIC_REPEAT = (1 << 3), +} eXrHapticFlag; + +/** For axis-based inputs (thumbstick/trackpad/etc). Determines the region for action execution + * (mutually exclusive per axis). */ 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; +typedef enum eXrPoseFlag { + /* Pose represents controller grip/aim. */ + XR_POSE_GRIP = (1 << 0), + XR_POSE_AIM = (1 << 1), +} eXrPoseFlag; + +/* -------------------------------------------------------------------- */ + +typedef struct XrActionMapBinding { + struct XrActionMapBinding *next, *prev; + + /** Unique name. */ + char name[64]; /* MAX_NAME */ + + /** OpenXR interaction profile path. */ + char profile[256]; + /** OpenXR component paths. */ + char component_path0[192]; + char component_path1[192]; + + /** Input threshold/region. */ + float float_threshold; + short axis_flag; /* eXrAxisFlag */ + char _pad[2]; + + /** Pose action properties. */ + float pose_location[3]; + float pose_rotation[3]; +} XrActionMapBinding; + +/* -------------------------------------------------------------------- */ + +typedef struct XrActionMapItem { + struct XrActionMapItem *next, *prev; + + /** Unique name. */ + char name[64]; /* MAX_NAME */ + /** Type. */ + char type; /** eXrActionType */ + char _pad[7]; + + /** OpenXR user paths. */ + char user_path0[64]; + char user_path1[64]; + + /** Operator to be called on XR events. */ + char op[64]; /* OP_MAX_TYPENAME */ + /** Operator properties, assigned to ptr->data and can be written to a file. */ + IDProperty *op_properties; + /** RNA pointer to access properties. */ + struct PointerRNA *op_properties_ptr; + + short op_flag; /* eXrOpFlag */ + short action_flag; /* eXrActionFlag */ + short haptic_flag; /* eXrHapticFlag */ + + /** Pose action properties. */ + short pose_flag; /* eXrPoseFlag */ + + /** Haptic properties. */ + char haptic_name[64]; /* MAX_NAME */ + float haptic_duration; + float haptic_frequency; + float haptic_amplitude; + + short selbinding; + char _pad3[2]; + ListBase bindings; /* XrActionMapBinding */ +} XrActionMapItem; + +/* -------------------------------------------------------------------- */ + +typedef struct XrActionMap { + struct XrActionMap *next, *prev; + + /** Unique name. */ + char name[64]; /* MAX_NAME */ + + ListBase items; /* XrActionMapItem */ + short selitem; + char _pad[6]; +} XrActionMap; + +/* -------------------------------------------------------------------- */ + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 56e8418972c..358db14c298 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -18,9 +18,14 @@ * \ingroup RNA */ +#include "BLI_math.h" + +#include "DNA_space_types.h" #include "DNA_view3d_types.h" +#include "DNA_windowmanager_types.h" #include "DNA_xr_types.h" +#include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -30,10 +35,462 @@ #ifdef RNA_RUNTIME -# include "BLI_math.h" +# include "BLI_listbase.h" # include "WM_api.h" +/* -------------------------------------------------------------------- */ +/** \name XR Action Map + * \{ */ + +static XrActionMapBinding *rna_XrActionMapBinding_new(XrActionMapItem *ami, + const char *name, + bool replace_existing) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_binding_new(ami, name, replace_existing); +# else + UNUSED_VARS(ami, name, replace_existing); + return NULL; +# endif +} + +static XrActionMapBinding *rna_XrActionMapBinding_new_from_binding(XrActionMapItem *ami, + XrActionMapBinding *amb_src) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_binding_add_copy(ami, amb_src); +# else + UNUSED_VARS(ami, amb_src); + return NULL; +# endif +} + +static void rna_XrActionMapBinding_remove(XrActionMapItem *ami, + ReportList *reports, + PointerRNA *amb_ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = amb_ptr->data; + if (WM_xr_actionmap_binding_remove(ami, amb) == false) { + BKE_reportf(reports, + RPT_ERROR, + "ActionMapBinding '%s' cannot be removed from '%s'", + amb->name, + ami->name); + return; + } + RNA_POINTER_INVALIDATE(amb_ptr); +# else + UNUSED_VARS(ami, reports, amb_ptr); +# endif +} + +static XrActionMapBinding *rna_XrActionMapBinding_find(XrActionMapItem *ami, const char *name) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_binding_find(ami, name); +# else + UNUSED_VARS(ami, name); + return NULL; +# endif +} + +static int rna_XrActionMapBinding_axis0_flag_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + if ((amb->axis_flag & XR_AXIS0_POS) != 0) { + return XR_AXIS0_POS; + } + if ((amb->axis_flag & XR_AXIS0_NEG) != 0) { + return XR_AXIS0_NEG; + } +# else + UNUSED_VARS(ptr); +# endif + return 0; +} + +static void rna_XrActionMapBinding_axis0_flag_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + amb->axis_flag &= ~(XR_AXIS0_POS | XR_AXIS0_NEG); + amb->axis_flag |= value; +# else + UNUSED_VARS(ptr, value); +# endif +} + +static int rna_XrActionMapBinding_axis1_flag_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + if ((amb->axis_flag & XR_AXIS1_POS) != 0) { + return XR_AXIS1_POS; + } + if ((amb->axis_flag & XR_AXIS1_NEG) != 0) { + return XR_AXIS1_NEG; + } +# else + UNUSED_VARS(ptr); +# endif + return 0; +} + +static void rna_XrActionMapBinding_axis1_flag_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + amb->axis_flag &= ~(XR_AXIS1_POS | XR_AXIS1_NEG); + amb->axis_flag |= value; +# else + UNUSED_VARS(ptr, value); +# endif +} + +static void rna_XrActionMapBinding_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->xr.runtime) { + ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime); + short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime); + XrActionMap *actionmap = BLI_findlink(actionmaps, idx); + if (actionmap) { + XrActionMapItem *ami = BLI_findlink(&actionmap->items, actionmap->selitem); + if (ami) { + XrActionMapBinding *amb = ptr->data; + WM_xr_actionmap_binding_ensure_unique(ami, amb); + } + } + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +static XrActionMapItem *rna_XrActionMapItem_new(XrActionMap *am, + const char *name, + bool replace_existing) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_item_new(am, name, replace_existing); +# else + UNUSED_VARS(am, name, replace_existing); + return NULL; +# endif +} + +static XrActionMapItem *rna_XrActionMapItem_new_from_item(XrActionMap *am, + XrActionMapItem *ami_src) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_item_add_copy(am, ami_src); +# else + UNUSED_VARS(am, ami_src); + return NULL; +# endif +} + +static void rna_XrActionMapItem_remove(XrActionMap *am, ReportList *reports, PointerRNA *ami_ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ami_ptr->data; + if (WM_xr_actionmap_item_remove(am, ami) == false) { + BKE_reportf( + reports, RPT_ERROR, "ActionMapItem '%s' cannot be removed from '%s'", ami->name, am->name); + return; + } + RNA_POINTER_INVALIDATE(ami_ptr); +# else + UNUSED_VARS(am, reports, ami_ptr); +# endif +} + +static XrActionMapItem *rna_XrActionMapItem_find(XrActionMap *am, const char *name) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_item_find(am, name); +# else + UNUSED_VARS(am, name); + return NULL; +# endif +} + +static void rna_XrActionMapItem_op_name_get(PointerRNA *ptr, char *value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (ami->op[0]) { + if (ami->op_properties_ptr) { + wmOperatorType *ot = WM_operatortype_find(ami->op, 1); + if (ot) { + strcpy(value, WM_operatortype_name(ot, ami->op_properties_ptr)); + return; + } + } + strcpy(value, ami->op); + return; + } +# else + UNUSED_VARS(ptr); +# endif + value[0] = '\0'; +} + +static int rna_XrActionMapItem_op_name_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (ami->op[0]) { + if (ami->op_properties_ptr) { + wmOperatorType *ot = WM_operatortype_find(ami->op, 1); + if (ot) { + return strlen(WM_operatortype_name(ot, ami->op_properties_ptr)); + } + } + return strlen(ami->op); + } +# else + UNUSED_VARS(ptr); +# endif + return 0; +} + +static PointerRNA rna_XrActionMapItem_op_properties_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (ami->op_properties_ptr) { + return *(ami->op_properties_ptr); + } +# else + UNUSED_VARS(ptr); +# endif + return PointerRNA_NULL; +} + +static bool rna_XrActionMapItem_bimanual_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->action_flag & XR_ACTION_BIMANUAL) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_bimanual_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (value) { + ami->action_flag |= XR_ACTION_BIMANUAL; + } + else { + ami->action_flag &= ~XR_ACTION_BIMANUAL; + } +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrActionMapItem_haptic_match_user_paths_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->haptic_flag & XR_HAPTIC_MATCHUSERPATHS) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_haptic_match_user_paths_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (value) { + ami->haptic_flag |= XR_HAPTIC_MATCHUSERPATHS; + } + else { + ami->haptic_flag &= ~XR_HAPTIC_MATCHUSERPATHS; + } +# else + UNUSED_VARS(ptr, value); +# endif +} + +static int rna_XrActionMapItem_haptic_flag_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->haptic_flag & XR_HAPTIC_RELEASE) != 0) { + return ((ami->haptic_flag & XR_HAPTIC_PRESS) != 0) ? (XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE) : + XR_HAPTIC_RELEASE; + } + if ((ami->haptic_flag & XR_HAPTIC_REPEAT) != 0) { + return XR_HAPTIC_REPEAT; + } +# else + UNUSED_VARS(ptr); +# endif + return XR_HAPTIC_PRESS; +} + +static void rna_XrActionMapItem_haptic_flag_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + ami->haptic_flag &= ~(XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE | XR_HAPTIC_REPEAT); + ami->haptic_flag |= value; +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrActionMapItem_pose_is_controller_grip_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->pose_flag & XR_POSE_GRIP) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_pose_is_controller_grip_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + SET_FLAG_FROM_TEST(ami->pose_flag, value, XR_POSE_GRIP); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrActionMapItem_pose_is_controller_aim_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->pose_flag & XR_POSE_AIM) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_pose_is_controller_aim_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + SET_FLAG_FROM_TEST(ami->pose_flag, value, XR_POSE_AIM); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static void rna_XrActionMapItem_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->xr.runtime) { + ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime); + short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime); + XrActionMap *actionmap = BLI_findlink(actionmaps, idx); + if (actionmap) { + XrActionMapItem *ami = ptr->data; + WM_xr_actionmap_item_ensure_unique(actionmap, ami); + } + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +static void rna_XrActionMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + WM_xr_actionmap_item_properties_update_ot(ami); +# else + UNUSED_VARS(ptr); +# endif +} + +static XrActionMap *rna_XrActionMap_new(wmXrData *xr, const char *name, bool replace_existing) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_new(xr->runtime, name, replace_existing); +# else + UNUSED_VARS(xr, name, replace_existing); + return NULL; +# endif +} + +static XrActionMap *rna_XrActionMap_new_from_actionmap(wmXrData *xr, XrActionMap *am_src) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_add_copy(xr->runtime, am_src); +# else + UNUSED_VARS(xr, am_src); + return NULL; +# endif +} + +static void rna_XrActionMap_remove(wmXrData *xr, ReportList *reports, PointerRNA *actionmap_ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMap *actionmap = actionmap_ptr->data; + if (WM_xr_actionmap_remove(xr->runtime, actionmap) == false) { + BKE_reportf(reports, RPT_ERROR, "ActionMap '%s' cannot be removed", actionmap->name); + return; + } + RNA_POINTER_INVALIDATE(actionmap_ptr); +# else + UNUSED_VARS(xr, reports, actionmap_ptr); +# endif +} + +static XrActionMap *rna_XrActionMap_find(wmXrData *xr, const char *name) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_find(xr->runtime, name); +# else + UNUSED_VARS(xr, name); + return NULL; +# endif +} + +static void rna_XrActionMap_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->xr.runtime) { + XrActionMap *actionmap = ptr->data; + WM_xr_actionmap_ensure_unique(wm->xr.runtime, actionmap); + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + # ifdef WITH_XR_OPENXR static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) { @@ -49,6 +506,10 @@ static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) } # endif +/* -------------------------------------------------------------------- */ +/** \name XR Session Settings + * \{ */ + static bool rna_XrSessionSettings_use_positional_tracking_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -91,6 +552,12 @@ static void rna_XrSessionSettings_use_absolute_tracking_set(PointerRNA *ptr, boo # endif } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session State + * \{ */ + static bool rna_XrSessionState_is_running(bContext *C) { # ifdef WITH_XR_OPENXR @@ -112,6 +579,304 @@ static void rna_XrSessionState_reset_to_base_pose(bContext *C) # endif } +static bool rna_XrSessionState_action_set_create(bContext *C, XrActionMap *actionmap) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_action_set_create(&wm->xr, actionmap->name); +# else + UNUSED_VARS(C, actionmap); + return false; +# endif +} + +static bool rna_XrSessionState_action_create(bContext *C, + XrActionMap *actionmap, + XrActionMapItem *ami) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + unsigned int count_subaction_paths = 0; + const char *subaction_paths[2]; + + if (ami->user_path0[0]) { + subaction_paths[0] = ami->user_path0; + ++count_subaction_paths; + + if (ami->user_path1[0]) { + subaction_paths[1] = ami->user_path1; + ++count_subaction_paths; + } + } + else { + if (ami->user_path1[0]) { + subaction_paths[0] = ami->user_path1; + ++count_subaction_paths; + } + else { + return false; + } + } + + const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); + const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT); + wmOperatorType *ot = NULL; + IDProperty *op_properties = NULL; + const char *haptic_name = NULL; + int64_t haptic_duration_msec; + + if (is_button_action) { + if (ami->op[0]) { + char idname[OP_MAX_TYPENAME]; + WM_operator_bl_idname(idname, ami->op); + ot = WM_operatortype_find(idname, true); + if (ot) { + op_properties = ami->op_properties; + } + } + + haptic_name = &ami->haptic_name[0]; + haptic_duration_msec = (int64_t)(ami->haptic_duration * 1000.0f); + } + + return WM_xr_action_create(&wm->xr, + actionmap->name, + ami->name, + ami->type, + count_subaction_paths, + subaction_paths, + ot, + op_properties, + is_button_action ? &haptic_name : NULL, + is_button_action ? &haptic_duration_msec : NULL, + is_button_action ? &ami->haptic_frequency : NULL, + is_button_action ? &ami->haptic_amplitude : NULL, + ami->op_flag, + ami->action_flag, + ami->haptic_flag); +# else + UNUSED_VARS(C, actionmap, ami); + return false; +# endif +} + +static bool rna_XrSessionState_action_binding_create(bContext *C, + XrActionMap *actionmap, + XrActionMapItem *ami, + XrActionMapBinding *amb) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + unsigned int count_subaction_paths = 0; + const char *subaction_paths[2]; + const char *component_paths[2]; + + if (ami->user_path0[0]) { + subaction_paths[0] = ami->user_path0; + component_paths[0] = amb->component_path0; + ++count_subaction_paths; + + if (ami->user_path1[0]) { + subaction_paths[1] = ami->user_path1; + component_paths[1] = amb->component_path1; + ++count_subaction_paths; + } + } + else { + if (ami->user_path1[0]) { + subaction_paths[0] = ami->user_path1; + component_paths[0] = amb->component_path1; + ++count_subaction_paths; + } + else { + return false; + } + } + + const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); + const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT); + const bool is_pose_action = (ami->type == XR_POSE_INPUT); + float float_thresholds[2]; + eXrAxisFlag axis_flags[2]; + wmXrPose poses[2]; + + if (is_float_action) { + float_thresholds[0] = float_thresholds[1] = amb->float_threshold; + } + if (is_button_action) { + axis_flags[0] = axis_flags[1] = amb->axis_flag; + } + if (is_pose_action) { + copy_v3_v3(poses[0].position, amb->pose_location); + eul_to_quat(poses[0].orientation_quat, amb->pose_rotation); + normalize_qt(poses[0].orientation_quat); + memcpy(&poses[1], &poses[0], sizeof(poses[1])); + } + + return WM_xr_action_binding_create(&wm->xr, + actionmap->name, + ami->name, + amb->profile, + count_subaction_paths, + subaction_paths, + component_paths, + is_float_action ? float_thresholds : NULL, + is_button_action ? axis_flags : NULL, + is_pose_action ? poses : NULL); +# else + UNUSED_VARS(C, actionmap, ami, amb); + return false; +# endif +} + +bool rna_XrSessionState_active_action_set_set(bContext *C, const char *action_set_name) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_active_action_set_set(&wm->xr, action_set_name); +# else + UNUSED_VARS(C, action_set_name); + return false; +# endif +} + +bool rna_XrSessionState_controller_pose_actions_set(bContext *C, + const char *action_set_name, + const char *grip_action_name, + const char *aim_action_name) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_controller_pose_actions_set( + &wm->xr, action_set_name, grip_action_name, aim_action_name); +# else + UNUSED_VARS(C, action_set_name, grip_action_name, aim_action_name); + return false; +# endif +} + +void rna_XrSessionState_action_state_get(bContext *C, + const char *action_set_name, + const char *action_name, + const char *user_path, + float r_state[2]) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + wmXrActionState state; + if (WM_xr_action_state_get(&wm->xr, action_set_name, action_name, user_path, &state)) { + switch (state.type) { + case XR_BOOLEAN_INPUT: + r_state[0] = (float)state.state_boolean; + r_state[1] = 0.0f; + return; + case XR_FLOAT_INPUT: + r_state[0] = state.state_float; + r_state[1] = 0.0f; + return; + case XR_VECTOR2F_INPUT: + copy_v2_v2(r_state, state.state_vector2f); + return; + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + } +# else + UNUSED_VARS(C, action_set_name, action_name, user_path); +# endif + zero_v2(r_state); +} + +bool rna_XrSessionState_haptic_action_apply(bContext *C, + const char *action_set_name, + const char *action_name, + const char *user_path, + float duration, + float frequency, + float amplitude) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + int64_t duration_msec = (int64_t)(duration * 1000.0f); + return WM_xr_haptic_action_apply(&wm->xr, + action_set_name, + action_name, + user_path[0] ? &user_path : NULL, + &duration_msec, + &frequency, + &litude); +# else + UNUSED_VARS(C, action_set_name, action_name, user_path, duration, frequency, amplitude); + return false; +# endif +} + +void rna_XrSessionState_haptic_action_stop(bContext *C, + const char *action_set_name, + const char *action_name, + const char *user_path) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_haptic_action_stop( + &wm->xr, action_set_name, action_name, user_path[0] ? &user_path : NULL); +# else + UNUSED_VARS(C, action_set_name, action_name, user_path); +# endif +} + +static void rna_XrSessionState_controller_grip_location_get(bContext *C, + int *index, + float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_grip_location_get(&wm->xr, *index, r_values); +# else + UNUSED_VARS(C, index); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_controller_grip_rotation_get(bContext *C, + int index, + float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_grip_rotation_get(&wm->xr, index, r_values); +# else + UNUSED_VARS(C, index); + unit_qt(r_values); +# endif +} + +static void rna_XrSessionState_controller_aim_location_get(bContext *C, + int *index, + float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_aim_location_get(&wm->xr, *index, r_values); +# else + UNUSED_VARS(C, index); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_controller_aim_rotation_get(bContext *C, int index, float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_aim_rotation_get(&wm->xr, index, r_values); +# else + UNUSED_VARS(C, index); + unit_qt(r_values); +# endif +} + static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *r_values) { # ifdef WITH_XR_OPENXR @@ -134,8 +899,486 @@ static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float * # endif } +static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + ListBase *lb = WM_xr_actionmaps_get(xr->runtime); + rna_iterator_listbase_begin(iter, lb, NULL); +} + +static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return WM_xr_actionmap_active_index_get(xr->runtime); +# else + UNUSED_VARS(ptr); + return -1; +# endif +} + +static void rna_XrSessionState_active_actionmap_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_actionmap_active_index_set(xr->runtime, (short)value); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static int rna_XrSessionState_selected_actionmap_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return WM_xr_actionmap_selected_index_get(xr->runtime); +# else + UNUSED_VARS(ptr); + return -1; +# endif +} + +static void rna_XrSessionState_selected_actionmap_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_actionmap_selected_index_set(xr->runtime, (short)value); +# else + UNUSED_VARS(ptr, value); +# endif +} + +/** \} */ + #else /* RNA_RUNTIME */ +/* -------------------------------------------------------------------- */ + +static const EnumPropertyItem rna_enum_xr_action_types[] = { + {XR_FLOAT_INPUT, + "FLOAT", + 0, + "Float", + "Float action, representing either a digital or analog button"}, + {XR_VECTOR2F_INPUT, + "VECTOR2D", + 0, + "Vector2D", + "2D float vector action, representing a thumbstick or trackpad"}, + {XR_POSE_INPUT, + "POSE", + 0, + "Pose", + "3D pose action, representing a controller's location and rotation"}, + {XR_VIBRATION_OUTPUT, + "VIBRATION", + 0, + "Vibration", + "Haptic vibration output action, to be applied with a duration, frequency, and amplitude"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_op_flags[] = { + {XR_OP_PRESS, + "PRESS", + 0, + "Press", + "Execute operator on button press (non-modal operators only)"}, + {XR_OP_RELEASE, + "RELEASE", + 0, + "Release", + "Execute operator on button release (non-modal operators only)"}, + {XR_OP_MODAL, "MODAL", 0, "Modal", "Use modal execution (modal operators only)"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_haptic_flags[] = { + {XR_HAPTIC_PRESS, "PRESS", 0, "Press", "Apply haptics on button press"}, + {XR_HAPTIC_RELEASE, "RELEASE", 0, "Release", "Apply haptics on button release"}, + {XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE, + "PRESS_RELEASE", + 0, + "Press Release", + "Apply haptics on button press and release"}, + {XR_HAPTIC_REPEAT, + "REPEAT", + 0, + "Repeat", + "Apply haptics repeatedly for the duration of the button press"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_axis0_flags[] = { + {0, "ANY", 0, "Any", "Use any axis region for operator execution"}, + {XR_AXIS0_POS, + "POSITIVE", + 0, + "Positive", + "Use positive axis region only for operator execution"}, + {XR_AXIS0_NEG, + "NEGATIVE", + 0, + "Negative", + "Use negative axis region only for operator execution"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_axis1_flags[] = { + {0, "ANY", 0, "Any", "Use any axis region for operator execution"}, + {XR_AXIS1_POS, + "POSITIVE", + 0, + "Positive", + "Use positive axis region only for operator execution"}, + {XR_AXIS1_NEG, + "NEGATIVE", + 0, + "Negative", + "Use negative axis region only for operator execution"}, + {0, NULL, 0, NULL, NULL}, +}; + +/* -------------------------------------------------------------------- */ +/** \name XR Action Map + * \{ */ + +static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrActionMapBindings"); + srna = RNA_def_struct(brna, "XrActionMapBindings", NULL); + RNA_def_struct_sdna(srna, "XrActionMapItem"); + RNA_def_struct_ui_text(srna, "XR Action Map Bindings", "Collection of XR action map bindings"); + + func = RNA_def_function(srna, "new", "rna_XrActionMapBinding_new"); + parm = RNA_def_string(func, "name", NULL, 0, "Name of the action map binding", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "replace_existing", + true, + "Replace Existing", + "Replace any existing binding with the same name"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "binding", "XrActionMapBinding", "Binding", "Added action map binding"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_from_binding", "rna_XrActionMapBinding_new_from_binding"); + parm = RNA_def_pointer( + func, "binding", "XrActionMapBinding", "Binding", "Binding to use as a reference"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "result", "XrActionMapBinding", "Binding", "Added action map binding"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrActionMapBinding_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "binding", "XrActionMapBinding", "Binding", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrActionMapBinding_find"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "binding", + "XrActionMapBinding", + "Binding", + "The action map binding with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_actionmap_items(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrActionMapItems"); + srna = RNA_def_struct(brna, "XrActionMapItems", NULL); + RNA_def_struct_sdna(srna, "XrActionMap"); + RNA_def_struct_ui_text(srna, "XR Action Map Items", "Collection of XR action map items"); + + func = RNA_def_function(srna, "new", "rna_XrActionMapItem_new"); + parm = RNA_def_string(func, "name", NULL, 0, "Name of the action map item", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "replace_existing", + true, + "Replace Existing", + "Replace any existing item with the same name"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "Added action map item"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_from_item", "rna_XrActionMapItem_new_from_item"); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "Item to use as a reference"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "result", "XrActionMapItem", "Item", "Added action map item"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrActionMapItem_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrActionMapItem_find"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "item", "XrActionMapItem", "Item", "The action map item with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_actionmaps(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrActionMaps"); + srna = RNA_def_struct(brna, "XrActionMaps", NULL); + RNA_def_struct_sdna(srna, "wmXrData"); + RNA_def_struct_ui_text(srna, "XR Action Maps", "Collection of XR action maps"); + + func = RNA_def_function(srna, "new", "rna_XrActionMap_new"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "replace_existing", + true, + "Replace Existing", + "Replace any existing actionmap with the same name"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Added action map"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_from_actionmap", "rna_XrActionMap_new_from_actionmap"); + parm = RNA_def_pointer( + func, "actionmap", "XrActionMap", "Action Map", "Action map to use as a reference"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "result", "XrActionMap", "Action Map", "Added action map"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrActionMap_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Removed action map"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrActionMap_find"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "actionmap", "XrActionMap", "Action Map", "The action map with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_actionmap(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* XrActionMap */ + srna = RNA_def_struct(brna, "XrActionMap", NULL); + RNA_def_struct_sdna(srna, "XrActionMap"); + RNA_def_struct_ui_text(srna, "XR Action Map", ""); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the action map"); + RNA_def_property_update(prop, 0, "rna_XrActionMap_name_update"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "actionmap_items", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "items", NULL); + RNA_def_property_struct_type(prop, "XrActionMapItem"); + RNA_def_property_ui_text( + prop, + "Items", + "Items in the action map, mapping an XR event to an operator, pose, or haptic output"); + rna_def_xr_actionmap_items(brna, prop); + + prop = RNA_def_property(srna, "selected_item", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "selitem"); + RNA_def_property_ui_text(prop, "Selected Item", ""); + + /* XrActionMapItem */ + srna = RNA_def_struct(brna, "XrActionMapItem", NULL); + RNA_def_struct_sdna(srna, "XrActionMapItem"); + RNA_def_struct_ui_text(srna, "XR Action Map Item", ""); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the action map item"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_name_update"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_xr_action_types); + RNA_def_property_ui_text(prop, "Type", "Action type"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); + + prop = RNA_def_property(srna, "user_path0", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 64); + RNA_def_property_ui_text(prop, "User Path 0", "OpenXR user path"); + + prop = RNA_def_property(srna, "user_path1", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 64); + RNA_def_property_ui_text(prop, "User Path 1", "OpenXR user path"); + + prop = RNA_def_property(srna, "op", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); + RNA_def_property_ui_text(prop, "Operator", "Identifier of operator to call on action event"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); + + prop = RNA_def_property(srna, "op_name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "Operator Name", "Name of operator (translated) to call on action event"); + RNA_def_property_string_funcs( + prop, "rna_XrActionMapItem_op_name_get", "rna_XrActionMapItem_op_name_length", NULL); + + prop = RNA_def_property(srna, "op_properties", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "OperatorProperties"); + RNA_def_property_pointer_funcs(prop, "rna_XrActionMapItem_op_properties_get", NULL, NULL, NULL); + RNA_def_property_ui_text( + prop, "Operator Properties", "Properties to set when the operator is called"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); + + prop = RNA_def_property(srna, "op_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "op_flag"); + RNA_def_property_enum_items(prop, rna_enum_xr_op_flags); + RNA_def_property_ui_text(prop, "Operator Mode", "Operator execution mode"); + + prop = RNA_def_property(srna, "bimanual", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs( + prop, "rna_XrActionMapItem_bimanual_get", "rna_XrActionMapItem_bimanual_set"); + RNA_def_property_ui_text( + prop, "Bimanual", "The action depends on the states/poses of both user paths"); + + prop = RNA_def_property(srna, "pose_is_controller_grip", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrActionMapItem_pose_is_controller_grip_get", + "rna_XrActionMapItem_pose_is_controller_grip_set"); + RNA_def_property_ui_text( + prop, "Is Controller Grip", "The action poses will be used for the VR controller grips"); + + prop = RNA_def_property(srna, "pose_is_controller_aim", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrActionMapItem_pose_is_controller_aim_get", + "rna_XrActionMapItem_pose_is_controller_aim_set"); + RNA_def_property_ui_text( + prop, "Is Controller Aim", "The action poses will be used for the VR controller aims"); + + prop = RNA_def_property(srna, "haptic_name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text( + prop, "Haptic Name", "Name of the haptic action to apply when executing this action"); + + prop = RNA_def_property(srna, "haptic_match_user_paths", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrActionMapItem_haptic_match_user_paths_get", + "rna_XrActionMapItem_haptic_match_user_paths_set"); + RNA_def_property_ui_text( + prop, + "Haptic Match User Paths", + "Apply haptics to the same user paths for the haptic action and this action"); + + prop = RNA_def_property(srna, "haptic_duration", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_text(prop, + "Haptic Duration", + "Haptic duration in seconds. 0.0 is the minimum supported duration"); + + prop = RNA_def_property(srna, "haptic_frequency", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_text(prop, + "Haptic Frequency", + "Frequency of the haptic vibration in hertz. 0.0 specifies the OpenXR " + "runtime's default frequency"); + + prop = RNA_def_property(srna, "haptic_amplitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text( + prop, "Haptic Amplitude", "Intensity of the haptic vibration, ranging from 0.0 to 1.0"); + + prop = RNA_def_property(srna, "haptic_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_xr_haptic_flags); + RNA_def_property_enum_funcs( + prop, "rna_XrActionMapItem_haptic_flag_get", "rna_XrActionMapItem_haptic_flag_set", NULL); + RNA_def_property_ui_text(prop, "Haptic mode", "Haptic application mode"); + + prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrActionMapBinding"); + RNA_def_property_ui_text( + prop, "Bindings", "Bindings for the action map item, mapping the action to an XR input"); + rna_def_xr_actionmap_bindings(brna, prop); + + prop = RNA_def_property(srna, "selected_binding", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "selbinding"); + RNA_def_property_ui_text(prop, "Selected Binding", "Currently selected binding"); + + /* XrActionMapBinding */ + srna = RNA_def_struct(brna, "XrActionMapBinding", NULL); + RNA_def_struct_sdna(srna, "XrActionMapBinding"); + RNA_def_struct_ui_text(srna, "XR Action Map Binding", "Binding in an XR action map item"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the action map binding"); + RNA_def_property_update(prop, 0, "rna_XrActionMapBinding_name_update"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "profile", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 256); + RNA_def_property_ui_text(prop, "Profile", "OpenXR interaction profile path"); + + prop = RNA_def_property(srna, "component_path0", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 192); + RNA_def_property_ui_text(prop, "Component Path 0", "OpenXR component path"); + + prop = RNA_def_property(srna, "component_path1", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 192); + RNA_def_property_ui_text(prop, "Component Path 1", "OpenXR component path"); + + prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "float_threshold"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Threshold", "Input threshold for button/axis actions"); + + prop = RNA_def_property(srna, "axis0_region", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_xr_axis0_flags); + RNA_def_property_enum_funcs(prop, + "rna_XrActionMapBinding_axis0_flag_get", + "rna_XrActionMapBinding_axis0_flag_set", + NULL); + RNA_def_property_ui_text( + prop, "Axis 0 Region", "Action execution region for the first input axis"); + + prop = RNA_def_property(srna, "axis1_region", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_xr_axis1_flags); + RNA_def_property_enum_funcs(prop, + "rna_XrActionMapBinding_axis1_flag_get", + "rna_XrActionMapBinding_axis1_flag_set", + NULL); + RNA_def_property_ui_text( + prop, "Axis 1 Region", "Action execution region for the second input axis"); + + prop = RNA_def_property(srna, "pose_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_ui_text(prop, "Pose Location Offset", ""); + + prop = RNA_def_property(srna, "pose_rotation", PROP_FLOAT, PROP_EULER); + RNA_def_property_ui_text(prop, "Pose Rotation Offset", ""); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session Settings + * \{ */ + static void rna_def_xr_session_settings(BlenderRNA *brna) { StructRNA *srna; @@ -241,6 +1484,12 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session State + * \{ */ + static void rna_def_xr_session_state(BlenderRNA *brna) { StructRNA *srna; @@ -265,6 +1514,260 @@ static void rna_def_xr_session_state(BlenderRNA *brna) parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + func = RNA_def_function(srna, "action_set_create", "rna_XrSessionState_action_set_create"); + RNA_def_function_ui_description(func, "Create a VR action set"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "action_create", "rna_XrSessionState_action_create"); + RNA_def_function_ui_description(func, "Create a VR action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap_item", "XrActionMapItem", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "action_binding_create", "rna_XrSessionState_action_binding_create"); + RNA_def_function_ui_description(func, "Create a VR action binding"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap_item", "XrActionMapItem", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap_binding", "XrActionMapBinding", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "active_action_set_set", "rna_XrSessionState_active_action_set_set"); + RNA_def_function_ui_description(func, "Set the active VR action set"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "controller_pose_actions_set", "rna_XrSessionState_controller_pose_actions_set"); + RNA_def_function_ui_description(func, "Set the actions that determine the VR controller poses"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, + "grip_action", + NULL, + 64, + "Grip Action", + "Name of the action representing the controller grips"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, + "aim_action", + NULL, + 64, + "Aim Action", + "Name of the action representing the controller aims"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "action_state_get", "rna_XrSessionState_action_state_get"); + RNA_def_function_ui_description(func, "Get the current state of a VR action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "user_path", NULL, 64, "User Path", "OpenXR user path"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_float_array( + func, + "state", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Action State", + "Current state of the VR action. Second float value is only set for 2D vector type actions", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function(srna, "haptic_action_apply", "rna_XrSessionState_haptic_action_apply"); + RNA_def_function_ui_description(func, "Apply a VR haptic action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, + "user_path", + NULL, + 64, + "User Path", + "Optional OpenXR user path. If not set, the action will be applied to all paths"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_float(func, + "duration", + 0.0f, + 0.0f, + FLT_MAX, + "Duration", + "Haptic duration in seconds. 0.0 is the minimum supported duration", + 0.0f, + FLT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float(func, + "frequency", + 0.0f, + 0.0f, + FLT_MAX, + "Frequency", + "Frequency of the haptic vibration in hertz. 0.0 specifies the OpenXR " + "runtime's default frequency", + 0.0f, + FLT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float(func, + "amplitude", + 1.0f, + 0.0f, + 1.0f, + "Amplitude", + "Haptic amplitude, ranging from 0.0 to 1.0", + 0.0f, + 1.0f); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "haptic_action_stop", "rna_XrSessionState_haptic_action_stop"); + RNA_def_function_ui_description(func, "Stop a VR haptic action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, + "user_path", + NULL, + 64, + "User Path", + "Optional OpenXR user path. If not set, the action will be stopped for all paths"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + + func = RNA_def_function( + srna, "controller_grip_location_get", "rna_XrSessionState_controller_grip_location_get"); + RNA_def_function_ui_description(func, + "Get the last known controller grip location in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + RNA_def_float_translation(func, + "location", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Controller grip location", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function( + srna, "controller_grip_rotation_get", "rna_XrSessionState_controller_grip_rotation_get"); + RNA_def_function_ui_description( + func, "Get the last known controller grip rotation (quaternion) in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float_vector(func, + "rotation", + 4, + NULL, + -FLT_MAX, + FLT_MAX, + "Rotation", + "Controller grip quaternion rotation", + -FLT_MAX, + FLT_MAX); + parm->subtype = PROP_QUATERNION; + RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function( + srna, "controller_aim_location_get", "rna_XrSessionState_controller_aim_location_get"); + RNA_def_function_ui_description(func, + "Get the last known controller aim location in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + RNA_def_float_translation(func, + "location", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Controller aim location", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function( + srna, "controller_aim_rotation_get", "rna_XrSessionState_controller_aim_rotation_get"); + RNA_def_function_ui_description( + func, "Get the last known controller aim rotation (quaternion) in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float_vector(func, + "rotation", + 4, + NULL, + -FLT_MAX, + FLT_MAX, + "Rotation", + "Controller aim quaternion rotation", + -FLT_MAX, + FLT_MAX); + parm->subtype = PROP_QUATERNION; + RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + prop = RNA_def_property(srna, "viewer_pose_location", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_array(prop, 3); RNA_def_property_float_funcs(prop, "rna_XrSessionState_viewer_pose_location_get", NULL, NULL); @@ -282,12 +1785,43 @@ static void rna_def_xr_session_state(BlenderRNA *brna) prop, "Viewer Pose Rotation", "Last known rotation of the viewer pose (center between the eyes) in world space"); + + prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, + "rna_XrSessionState_actionmaps_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + NULL, + NULL, + NULL, + NULL); + RNA_def_property_struct_type(prop, "XrActionMap"); + RNA_def_property_ui_text(prop, "XR Action Maps", ""); + rna_def_xr_actionmaps(brna, prop); + + prop = RNA_def_property(srna, "active_actionmap", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, + "rna_XrSessionState_active_actionmap_get", + "rna_XrSessionState_active_actionmap_set", + NULL); + RNA_def_property_ui_text(prop, "Active Action Map", ""); + + prop = RNA_def_property(srna, "selected_actionmap", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, + "rna_XrSessionState_selected_actionmap_get", + "rna_XrSessionState_selected_actionmap_set", + NULL); + RNA_def_property_ui_text(prop, "Selected Action Map", ""); } +/** \} */ + void RNA_def_xr(BlenderRNA *brna) { RNA_define_animate_sdna(false); + rna_def_xr_actionmap(brna); rna_def_xr_session_settings(brna); rna_def_xr_session_state(brna); diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 8b0ceb02b5f..b7fbb9bb82b 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -204,6 +204,7 @@ if(WITH_XR_OPENXR) list(APPEND SRC xr/intern/wm_xr.c xr/intern/wm_xr_action.c + xr/intern/wm_xr_actionmap.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 7e26d921bd7..02e8d42e0ff 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -74,8 +74,7 @@ struct wmNDOFMotionData; #endif #ifdef WITH_XR_OPENXR -struct wmXrActionState; -struct wmXrPose; +struct wmXrRuntimeData; #endif typedef struct wmGizmo wmGizmo; @@ -988,7 +987,13 @@ bool WM_xr_action_create(wmXrData *xr, const char **subaction_paths, struct wmOperatorType *ot, struct IDProperty *op_properties, - eXrOpFlag op_flag); + const char **haptic_name, + const int64_t *haptic_duration, + const float *haptic_frequency, + const float *haptic_amplitude, + eXrOpFlag op_flag, + eXrActionFlag action_flag, + eXrHapticFlag haptic_flag); void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name); bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, @@ -1022,10 +1027,48 @@ bool WM_xr_action_state_get(const wmXrData *xr, bool WM_xr_haptic_action_apply(wmXrData *xr, const char *action_set_name, const char *action_name, + const char **subaction_path, const int64_t *duration, const float *frequency, const float *amplitude); -void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name); +void WM_xr_haptic_action_stop(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char **subaction_path); + +/* wm_xr_actionmap.c */ +XrActionMap *WM_xr_actionmap_new(struct wmXrRuntimeData *runtime, + const char *name, + bool replace_existing); +void WM_xr_actionmap_ensure_unique(struct wmXrRuntimeData *runtime, XrActionMap *actionmap); +XrActionMap *WM_xr_actionmap_add_copy(struct wmXrRuntimeData *runtime, XrActionMap *am_src); +bool WM_xr_actionmap_remove(struct wmXrRuntimeData *runtime, XrActionMap *actionmap); +XrActionMap *WM_xr_actionmap_find(struct wmXrRuntimeData *runtime, const char *name); +void WM_xr_actionmap_clear(XrActionMap *actionmap); +void WM_xr_actionmaps_clear(struct wmXrRuntimeData *runtime); +ListBase *WM_xr_actionmaps_get(struct wmXrRuntimeData *runtime); +short WM_xr_actionmap_active_index_get(const struct wmXrRuntimeData *runtime); +void WM_xr_actionmap_active_index_set(struct wmXrRuntimeData *runtime, short idx); +short WM_xr_actionmap_selected_index_get(const struct wmXrRuntimeData *runtime); +void WM_xr_actionmap_selected_index_set(struct wmXrRuntimeData *runtime, short idx); + +XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap, + const char *name, + bool replace_existing); +void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami); +XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src); +bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami); +XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name); +void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami); + +XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami, + const char *name, + bool replace_existing); +void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb); +XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami, + XrActionMapBinding *amb_src); +bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb); +XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name); #endif /* WITH_XR_OPENXR */ #ifdef __cplusplus diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 2a67c2bee9f..716a0936a24 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -115,6 +115,7 @@ bool wm_xr_init(wmWindowManager *wm) void wm_xr_exit(wmWindowManager *wm) { if (wm->xr.runtime != NULL) { + WM_xr_actionmaps_clear(wm->xr.runtime); wm_xr_runtime_data_free(&wm->xr.runtime); } if (wm->xr.session_settings.shading.prop) { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index 8f2de4bbbad..2712fde51a8 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -23,6 +23,7 @@ * All functions are designed to be usable by RNA / the Python API. */ +#include "BLI_listbase.h" #include "BLI_math.h" #include "GHOST_C-api.h" @@ -56,6 +57,9 @@ static void action_set_destroy(void *val) MEM_SAFE_FREE(action_set->name); + BLI_freelistN(&action_set->active_modal_actions); + BLI_freelistN(&action_set->active_haptic_actions); + MEM_freeN(action_set); } @@ -70,7 +74,13 @@ static wmXrAction *action_create(const char *action_name, const char **subaction_paths, wmOperatorType *ot, IDProperty *op_properties, - eXrOpFlag op_flag) + const char **haptic_name, + const int64_t *haptic_duration, + const float *haptic_frequency, + const float *haptic_amplitude, + eXrOpFlag op_flag, + eXrActionFlag action_flag, + eXrHapticFlag haptic_flag) { wmXrAction *action = MEM_callocN(sizeof(*action), __func__); action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name"); @@ -121,7 +131,19 @@ static wmXrAction *action_create(const char *action_name, action->ot = ot; action->op_properties = op_properties; + + if (haptic_name) { + BLI_assert(is_button_action); + action->haptic_name = MEM_mallocN(strlen(*haptic_name) + 1, "XrAction_HapticName"); + strcpy(action->haptic_name, *haptic_name); + action->haptic_duration = *haptic_duration; + action->haptic_frequency = *haptic_frequency; + action->haptic_amplitude = *haptic_amplitude; + } + action->op_flag = op_flag; + action->action_flag = action_flag; + action->haptic_flag = haptic_flag; return action; } @@ -147,6 +169,8 @@ static void action_destroy(void *val) MEM_SAFE_FREE(action->float_thresholds); MEM_SAFE_FREE(action->axis_flags); + MEM_SAFE_FREE(action->haptic_name); + MEM_freeN(action); } @@ -190,9 +214,10 @@ void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) wm_xr_session_controller_data_clear(session_state); action_set->controller_grip_action = action_set->controller_aim_action = NULL; } - if (action_set->active_modal_action) { - action_set->active_modal_action = NULL; - } + + BLI_freelistN(&action_set->active_modal_actions); + BLI_freelistN(&action_set->active_haptic_actions); + session_state->active_action_set = NULL; } @@ -207,14 +232,31 @@ bool WM_xr_action_create(wmXrData *xr, const char **subaction_paths, wmOperatorType *ot, IDProperty *op_properties, - eXrOpFlag op_flag) + const char **haptic_name, + const int64_t *haptic_duration, + const float *haptic_frequency, + const float *haptic_amplitude, + eXrOpFlag op_flag, + eXrActionFlag action_flag, + eXrHapticFlag haptic_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); + wmXrAction *action = action_create(action_name, + type, + count_subaction_paths, + subaction_paths, + ot, + op_properties, + haptic_name, + haptic_duration, + haptic_frequency, + haptic_amplitude, + op_flag, + action_flag, + haptic_flag); GHOST_XrActionInfo info = { .name = action_name, @@ -274,9 +316,18 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char action_set->controller_grip_action = action_set->controller_aim_action = NULL; } - if (action_set->active_modal_action && - STREQ(action_set->active_modal_action->name, action_name)) { - action_set->active_modal_action = NULL; + LISTBASE_FOREACH (LinkData *, ld, &action_set->active_modal_actions) { + wmXrAction *active_modal_action = ld->data; + if (STREQ(active_modal_action->name, action_name)) { + BLI_freelinkN(&action_set->active_modal_actions, ld); + break; + } + } + + LISTBASE_FOREACH_MUTABLE (wmXrHapticAction *, ha, &action_set->active_haptic_actions) { + if (STREQ(ha->action->name, action_name)) { + BLI_freelinkN(&action_set->active_haptic_actions, ha); + } } GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name); @@ -342,16 +393,11 @@ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) } { - /* Unset active modal action (if any). */ + /* Clear any active modal/haptic actions. */ 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; - } + BLI_freelistN(&active_action_set->active_modal_actions); + BLI_freelistN(&active_action_set->active_haptic_actions); } } @@ -456,19 +502,28 @@ bool WM_xr_action_state_get(const wmXrData *xr, bool WM_xr_haptic_action_apply(wmXrData *xr, const char *action_set_name, const char *action_name, + const char **subaction_path, const int64_t *duration, const float *frequency, const float *amplitude) { - return GHOST_XrApplyHapticAction( - xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ? + return GHOST_XrApplyHapticAction(xr->runtime->context, + action_set_name, + action_name, + subaction_path, + duration, + frequency, + amplitude) ? true : false; } -void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name) +void WM_xr_haptic_action_stop(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char **subaction_path) { - GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name); + GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name, subaction_path); } /** \} */ /* XR-Action API */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c new file mode 100644 index 00000000000..7673f2aa212 --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c @@ -0,0 +1,565 @@ +/* + * 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 Action Maps + * + * XR actionmap API, similar to WM keymap API. + */ + +#include <math.h> +#include <string.h> + +#include "BKE_context.h" +#include "BKE_idprop.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "GHOST_Types.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_xr_intern.h" + +#define WM_XR_ACTIONMAP_STR_DEFAULT "actionmap" +#define WM_XR_ACTIONMAP_ITEM_STR_DEFAULT "action" +#define WM_XR_ACTIONMAP_BINDING_STR_DEFAULT "binding" + +/* -------------------------------------------------------------------- */ +/** \name Action Map Binding + * + * Binding in an XR action map item, that maps an action to an XR input. + * \{ */ + +XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami, + const char *name, + bool replace_existing) +{ + XrActionMapBinding *amb_prev = WM_xr_actionmap_binding_find(ami, name); + if (amb_prev && replace_existing) { + return amb_prev; + } + + XrActionMapBinding *amb = MEM_callocN(sizeof(XrActionMapBinding), __func__); + BLI_strncpy(amb->name, name, MAX_NAME); + if (amb_prev) { + WM_xr_actionmap_binding_ensure_unique(ami, amb); + } + + BLI_addtail(&ami->bindings, amb); + + /* Set non-zero threshold by default. */ + amb->float_threshold = 0.3f; + + return amb; +} + +static XrActionMapBinding *wm_xr_actionmap_binding_find_except(XrActionMapItem *ami, + const char *name, + XrActionMapBinding *ambexcept) +{ + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + if (STREQLEN(name, amb->name, MAX_NAME) && (amb != ambexcept)) { + return amb; + } + } + return NULL; +} + +/** + * Ensure unique name among all action map bindings. + */ +void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb) +{ + char name[MAX_NAME]; + char *suffix; + size_t baselen; + size_t idx = 0; + + BLI_strncpy(name, amb->name, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + + while (wm_xr_actionmap_binding_find_except(ami, name, amb)) { + if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { + /* Use default base name. */ + BLI_strncpy(name, WM_XR_ACTIONMAP_BINDING_STR_DEFAULT, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + idx = 0; + } + else { + BLI_snprintf(suffix, MAX_NAME, "%zu", idx); + } + } + + BLI_strncpy(amb->name, name, MAX_NAME); +} + +static XrActionMapBinding *wm_xr_actionmap_binding_copy(XrActionMapBinding *amb_src) +{ + XrActionMapBinding *amb_dst = MEM_dupallocN(amb_src); + + amb_dst->prev = amb_dst->next = NULL; + + return amb_dst; +} + +XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami, + XrActionMapBinding *amb_src) +{ + XrActionMapBinding *amb_dst = wm_xr_actionmap_binding_copy(amb_src); + + WM_xr_actionmap_binding_ensure_unique(ami, amb_dst); + + BLI_addtail(&ami->bindings, amb_dst); + + return amb_dst; +} + +bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb) +{ + int idx = BLI_findindex(&ami->bindings, amb); + + if (idx != -1) { + BLI_freelinkN(&ami->bindings, amb); + + if (BLI_listbase_is_empty(&ami->bindings)) { + ami->selbinding = -1; + } + else { + if (idx <= ami->selbinding) { + if (--ami->selbinding < 0) { + ami->selbinding = 0; + } + } + } + + return true; + } + + return false; +} + +XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name) +{ + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + if (STREQLEN(name, amb->name, MAX_NAME)) { + return amb; + } + } + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Action Map Item + * + * Item in an XR action map, that maps an XR event to an operator, pose, or haptic output. + * \{ */ + +static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami) +{ + WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op); + WM_operator_properties_sanitize(ami->op_properties_ptr, 1); +} + +static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami) +{ + if (ami->op_properties_ptr) { + WM_operator_properties_free(ami->op_properties_ptr); + MEM_freeN(ami->op_properties_ptr); + ami->op_properties_ptr = NULL; + ami->op_properties = NULL; + } + else { + BLI_assert(ami->op_properties == NULL); + } +} + +/** + * Similar to #wm_xr_actionmap_item_properties_set() + * but checks for the #eXrActionType and #wmOperatorType having changed. + */ +void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami) +{ + switch (ami->type) { + case XR_BOOLEAN_INPUT: + case XR_FLOAT_INPUT: + case XR_VECTOR2F_INPUT: + break; + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + wm_xr_actionmap_item_properties_free(ami); + memset(ami->op, 0, sizeof(ami->op)); + return; + } + + if (ami->op[0] == 0) { + wm_xr_actionmap_item_properties_free(ami); + return; + } + + if (ami->op_properties_ptr == NULL) { + wm_xr_actionmap_item_properties_set(ami); + } + else { + wmOperatorType *ot = WM_operatortype_find(ami->op, 0); + if (ot) { + if (ot->srna != ami->op_properties_ptr->type) { + /* Matches wm_xr_actionmap_item_properties_set() but doesn't alloc new ptr. */ + WM_operator_properties_create_ptr(ami->op_properties_ptr, ot); + if (ami->op_properties) { + ami->op_properties_ptr->data = ami->op_properties; + } + WM_operator_properties_sanitize(ami->op_properties_ptr, 1); + } + } + else { + wm_xr_actionmap_item_properties_free(ami); + } + } +} + +XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap, + const char *name, + bool replace_existing) +{ + XrActionMapItem *ami_prev = WM_xr_actionmap_item_find(actionmap, name); + if (ami_prev && replace_existing) { + wm_xr_actionmap_item_properties_free(ami_prev); + return ami_prev; + } + + XrActionMapItem *ami = MEM_callocN(sizeof(XrActionMapItem), __func__); + BLI_strncpy(ami->name, name, MAX_NAME); + if (ami_prev) { + WM_xr_actionmap_item_ensure_unique(actionmap, ami); + } + + BLI_addtail(&actionmap->items, ami); + + /* Set type to float (button) input by default. */ + ami->type = XR_FLOAT_INPUT; + + return ami; +} + +static XrActionMapItem *wm_xr_actionmap_item_find_except(XrActionMap *actionmap, + const char *name, + const XrActionMapItem *amiexcept) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + if (STREQLEN(name, ami->name, MAX_NAME) && (ami != amiexcept)) { + return ami; + } + } + return NULL; +} + +/** + * Ensure unique name among all action map items. + */ +void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami) +{ + char name[MAX_NAME]; + char *suffix; + size_t baselen; + size_t idx = 0; + + BLI_strncpy(name, ami->name, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + + while (wm_xr_actionmap_item_find_except(actionmap, name, ami)) { + if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { + /* Use default base name. */ + BLI_strncpy(name, WM_XR_ACTIONMAP_ITEM_STR_DEFAULT, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + idx = 0; + } + else { + BLI_snprintf(suffix, MAX_NAME, "%zu", idx); + } + } + + BLI_strncpy(ami->name, name, MAX_NAME); +} + +static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami) +{ + XrActionMapItem *amin = MEM_dupallocN(ami); + + amin->prev = amin->next = NULL; + + if (amin->op_properties) { + amin->op_properties_ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr"); + WM_operator_properties_create(amin->op_properties_ptr, amin->op); + + amin->op_properties = IDP_CopyProperty(amin->op_properties); + amin->op_properties_ptr->data = amin->op_properties; + } + else { + amin->op_properties = NULL; + amin->op_properties_ptr = NULL; + } + + return amin; +} + +XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src) +{ + XrActionMapItem *ami_dst = wm_xr_actionmap_item_copy(ami_src); + + WM_xr_actionmap_item_ensure_unique(actionmap, ami_dst); + + BLI_addtail(&actionmap->items, ami_dst); + + return ami_dst; +} + +bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami) +{ + int idx = BLI_findindex(&actionmap->items, ami); + + if (idx != -1) { + if (ami->op_properties_ptr) { + WM_operator_properties_free(ami->op_properties_ptr); + MEM_freeN(ami->op_properties_ptr); + } + BLI_freelinkN(&actionmap->items, ami); + + if (BLI_listbase_is_empty(&actionmap->items)) { + actionmap->selitem = -1; + } + else { + if (idx <= actionmap->selitem) { + if (--actionmap->selitem < 0) { + actionmap->selitem = 0; + } + } + } + + return true; + } + + return false; +} + +XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + if (STREQLEN(name, ami->name, MAX_NAME)) { + return ami; + } + } + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Action Map + * + * List of XR action map items. + * \{ */ + +XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, bool replace_existing) +{ + XrActionMap *am_prev = WM_xr_actionmap_find(runtime, name); + if (am_prev && replace_existing) { + WM_xr_actionmap_clear(am_prev); + return am_prev; + } + + XrActionMap *am = MEM_callocN(sizeof(struct XrActionMap), __func__); + BLI_strncpy(am->name, name, MAX_NAME); + if (am_prev) { + WM_xr_actionmap_ensure_unique(runtime, am); + } + + BLI_addtail(&runtime->actionmaps, am); + + return am; +} + +static XrActionMap *wm_xr_actionmap_find_except(wmXrRuntimeData *runtime, + const char *name, + const XrActionMap *am_except) +{ + LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + if (STREQLEN(name, am->name, MAX_NAME) && (am != am_except)) { + return am; + } + } + + return NULL; +} + +/** + * Ensure unique name among all action maps. + */ +void WM_xr_actionmap_ensure_unique(wmXrRuntimeData *runtime, XrActionMap *actionmap) +{ + char name[MAX_NAME]; + char *suffix; + size_t baselen; + size_t idx = 0; + + BLI_strncpy(name, actionmap->name, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + + while (wm_xr_actionmap_find_except(runtime, name, actionmap)) { + if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { + /* Use default base name. */ + BLI_strncpy(name, WM_XR_ACTIONMAP_STR_DEFAULT, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + idx = 0; + } + else { + BLI_snprintf(suffix, MAX_NAME, "%zu", idx); + } + } + + BLI_strncpy(actionmap->name, name, MAX_NAME); +} + +static XrActionMap *wm_xr_actionmap_copy(XrActionMap *am_src) +{ + XrActionMap *am_dst = MEM_dupallocN(am_src); + + am_dst->prev = am_dst->next = NULL; + BLI_listbase_clear(&am_dst->items); + + LISTBASE_FOREACH (XrActionMapItem *, ami, &am_src->items) { + XrActionMapItem *ami_new = wm_xr_actionmap_item_copy(ami); + BLI_addtail(&am_dst->items, ami_new); + } + + return am_dst; +} + +XrActionMap *WM_xr_actionmap_add_copy(wmXrRuntimeData *runtime, XrActionMap *am_src) +{ + XrActionMap *am_dst = wm_xr_actionmap_copy(am_src); + + WM_xr_actionmap_ensure_unique(runtime, am_dst); + + BLI_addtail(&runtime->actionmaps, am_dst); + + return am_dst; +} + +bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap) +{ + int idx = BLI_findindex(&runtime->actionmaps, actionmap); + + if (idx != -1) { + WM_xr_actionmap_clear(actionmap); + BLI_freelinkN(&runtime->actionmaps, actionmap); + + if (BLI_listbase_is_empty(&runtime->actionmaps)) { + runtime->actactionmap = runtime->selactionmap = -1; + } + else { + if (idx <= runtime->actactionmap) { + if (--runtime->actactionmap < 0) { + runtime->actactionmap = 0; + } + } + if (idx <= runtime->selactionmap) { + if (--runtime->selactionmap < 0) { + runtime->selactionmap = 0; + } + } + } + + return true; + } + + return false; +} + +XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name) +{ + LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + if (STREQLEN(name, am->name, MAX_NAME)) { + return am; + } + } + return NULL; +} + +void WM_xr_actionmap_clear(XrActionMap *actionmap) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + wm_xr_actionmap_item_properties_free(ami); + } + + BLI_freelistN(&actionmap->items); + + actionmap->selitem = -1; +} + +void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime) +{ + LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + WM_xr_actionmap_clear(am); + } + + BLI_freelistN(&runtime->actionmaps); + + runtime->actactionmap = runtime->selactionmap = -1; +} + +ListBase *WM_xr_actionmaps_get(wmXrRuntimeData *runtime) +{ + return &runtime->actionmaps; +} + +short WM_xr_actionmap_active_index_get(const wmXrRuntimeData *runtime) +{ + return runtime->actactionmap; +} + +void WM_xr_actionmap_active_index_set(wmXrRuntimeData *runtime, short idx) +{ + BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps)); + runtime->actactionmap = idx; +} + +short WM_xr_actionmap_selected_index_get(const wmXrRuntimeData *runtime) +{ + return runtime->selactionmap; +} + +void WM_xr_actionmap_selected_index_set(wmXrRuntimeData *runtime, short idx) +{ + BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps)); + runtime->selactionmap = idx; +} + +/** \} */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index b6aff1f71f9..4b1605d0f68 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -70,6 +70,10 @@ typedef struct wmXrRuntimeData { /** Although this struct is internal, RNA gets a handle to this for state information queries. */ wmXrSessionState session_state; wmXrSessionExitFn exit_fn; + + ListBase actionmaps; /* XrActionMap */ + short actactionmap; + short selactionmap; } wmXrRuntimeData; typedef struct wmXrViewportPair { @@ -99,6 +103,22 @@ typedef struct wmXrDrawData { float eye_position_ofs[3]; /* Local/view space. */ } wmXrDrawData; +typedef struct wmXrController { + struct wmXrController *next, *prev; + /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). + This subaction path will later be combined with a component path, and that combined path should + also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = + /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). + */ + char subaction_path[64]; + /* Pose (in world space) that represents the user's hand when holding the controller.*/ + GHOST_XrPose grip_pose; + float grip_mat[4][4]; + /* Pose (in world space) that represents the controller's aiming source. */ + GHOST_XrPose aim_pose; + float aim_mat[4][4]; +} wmXrController; + typedef struct wmXrAction { char *name; eXrActionType type; @@ -119,32 +139,37 @@ typedef struct wmXrAction { /** Operator to be called on XR events. */ struct wmOperatorType *ot; IDProperty *op_properties; + + /** Haptics. */ + char *haptic_name; + int64_t haptic_duration; + float haptic_frequency; + float haptic_amplitude; + + /** Flags. */ eXrOpFlag op_flag; + eXrActionFlag action_flag; + eXrHapticFlag haptic_flag; } wmXrAction; -typedef struct wmXrController { - struct wmXrController *next, *prev; - /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). - This subaction path will later be combined with a component path, and that combined path should - also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = - /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). - */ - char subaction_path[64]; - /* Pose (in world space) that represents the user's hand when holding the controller.*/ - GHOST_XrPose grip_pose; - float grip_mat[4][4]; - /* Pose (in world space) that represents the controller's aiming source. */ - GHOST_XrPose aim_pose; - float aim_mat[4][4]; -} wmXrController; +typedef struct wmXrHapticAction { + struct wmXrHapticAction *next, *prev; + wmXrAction *action; + const char **subaction_path; + int64_t time_start; +} wmXrHapticAction; typedef struct wmXrActionSet { char *name; + /** XR pose actions that determine the controller grip/aim transforms. */ wmXrAction *controller_grip_action; wmXrAction *controller_aim_action; - /** The currently active modal action (if any). */ - wmXrAction *active_modal_action; + + /** Currently active modal actions. */ + ListBase active_modal_actions; + /** Currently active haptic actions. */ + ListBase active_haptic_actions; } wmXrActionSet; wmXrRuntimeData *wm_xr_runtime_data_create(void); |